// version1.0 本包是用了抓取高德地图 景区围栏信息的

package gmap

import (
	"fmt"
	"time"
	"net/http"
	"log"
	//"github.com/PuerkitoBio/goquery"
	//"strconv"
	"net/url"
	"strings"
	"errors"
	"encoding/json"
	"math/rand"
	"os"
	"strconv"
)

//爬行的蜘蛛
type gspider struct {
	D         time.Duration //定时器
	TargetUrl string        //要爬的地址
	TempData  tempData      //临时的数据存放的地址
	Unindex   int64         //数据唯一的IP
	timess    int64
	t         *time.Ticker
	Scenic    *Scenic       //要爬的景区
	Start     int64
	End       int64
	TypeUrl   int64
}

type tempData struct {
	Status string `json:"status"`
	Data   interface{} `json:"data"`
}

const (
	NO_ERROR = iota
	ERR_FAST //短时间请求过快
	ERR_NO_BOUND   //没有围栏
	ERR_NO_MATCH  //结果无法解析
	ERR_NO_MATCH_ALL //结果完全没有匹配
	ERR_EMPTY   //结果是空的

)

var userAget []string
//获取一条景区数据
func init() {
	db = dbObject()
	userAget = []string{
		`Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50`,
		`Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50`,
		`Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;`,
		`Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)`,
		`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11`,
		`Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)`,
		`Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)`,
		`Mozilla/5.0 (X11; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0`,
		`Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)`,
	}
}

//开始爬行
func (gs *gspider) Run() {
	gs.parseArgs()
	gs.tt()
	for i := gs.Start; i <= gs.End; i++ {
		<-gs.t.C
		scenic := &Scenic{}
		if err := scenic.getScenic(i); err != nil {
			log.Println(fmt.Errorf("id:%d", i), err)
		} else {
			gs.Scenic = scenic
			gs.Unindex = gs.Scenic.ScenicId
			gs.TypeUrl = 0
			err := gs.httpGet(gs.parseUrl())
			if err == nil {
				gs.Scenic.upScenic()
				log.Printf("Type:%d   Id:%d status:ok\r\n", gs.TypeUrl, gs.Scenic.ScenicId)
			} else {
				gs.TypeUrl = 1
				err := gs.httpGet(gs.parseUrl())
				if err == nil {
					gs.Scenic.upScenic()
					log.Printf("Type:%d  Id:%d  status:ok\r\n", gs.TypeUrl, gs.Scenic.ScenicId)
				} else {
					gs.Scenic.upScenic()
				}
			}
		}
	}
}

//解析参数
func (gs *gspider)parseArgs() {
	args := os.Args
	if len(args) < 3 {
		log.Fatal("参数不合法\r\n")
	}
	s, err := strconv.Atoi(args[1])
	if err != nil {
		log.Fatal("请输入开始参数\r\n")
	}
	e, err := strconv.Atoi(args[2])
	if err != nil {
		log.Fatal("请输入结束参数\r\n")
	}
	gs.Start = int64(s)
	gs.End = int64(e)
}

func (gs *gspider) tt() {
	gs.t = time.NewTicker(gs.D)
}


//http 请求数据 正在抓取数据的 方法
func (gs *gspider) httpGet(goUrl string) error {
	client := &http.Client{}

	req, err := http.NewRequest("GET", goUrl, nil)

	if err != nil {

		log.Fatal(err)
	}

	req.Header.Set("User-Agent", gs.GetAgent())
	client.Timeout = time.Second * 30
	response, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()

	//解析抓取数据
	if response.StatusCode != 200 {
		log.Fatal(fmt.Sprintf("响应异常，响应码：%d", response.StatusCode))
	}

	de := json.NewDecoder(response.Body)

	if err := de.Decode(&gs.TempData); err != nil {
		log.Fatal(err)
	}

	if error_code := gs.parseTempData(); error_code != NO_ERROR {
		//解析失败
		gs.printErrorMsg(error_code)
		return errors.New("parse error")
	}
	return nil
}

func (gs *gspider)printErrorMsg(error_code int) {
	switch(error_code){
	case ERR_FAST:
		log.Printf("短时间内请求过多        Id:%d\r\n", gs.Unindex)
		time.Sleep(time.Second * 60)
	case ERR_NO_BOUND:
		log.Printf("Type:%d 景区没有围栏     Id:%d \r\n", gs.TypeUrl, gs.Unindex)
	case ERR_NO_MATCH:
		log.Printf("Type:%d 采集结果无法解析 Id:%d\r\n", gs.TypeUrl, gs.Unindex)
	default:
		log.Printf("Type:%d 解析采集结果错误 Id:%d\r\n %s", gs.TypeUrl, gs.Scenic.ScenicId, gs.TempData)
	}
}
//解析采集回来的临时数据
func (gs *gspider)parseTempData() int {
	//fmt.Println(gs.TempData)
	if gs.TempData.Status == "6" {
		return ERR_FAST
	}
	if gs.TempData.Status == "0" {
		return ERR_EMPTY
	}

	if gs.TempData.Status != "1" {
		return ERR_NO_MATCH_ALL
	}
	v, ok := gs.TempData.Data.([]interface{})
	if ok == false {
		return ERR_EMPTY
	}
	if len(v) <= 1 {
		return ERR_NO_BOUND
	}
	//fmt.Println(v)
	vv, ok := v[1].(map[string]interface{});
	if ok == false {
		return ERR_NO_BOUND
	}
	if map1, ok := vv["list"]; ok {
		if map2, ok := map1.([]interface{}); ok {
			if map3, ok := map2[0].(map[string]interface{}); ok {

				if map4, ok := map3["bound"]; ok {
					if map5, ok := map4.([]interface{}); ok {
						var temp_data []string
						for _, vs := range map5 {
							if v, ok := vs.(string); ok {
								temp_data = append(temp_data, v)
							}
						}
						gs.Scenic.BoundsNum = int64(len(temp_data))
						gs.Scenic.Bounds = strings.Join(temp_data, ";")
						return NO_ERROR
					} else {
						//fmt.Println(ok)
						return ERR_NO_BOUND
					}
				}
			}
		}
	}

	return ERR_NO_MATCH_ALL
}

func (gs *gspider)parseUrl() string {
	v := url.Values{}
	if gs.TypeUrl == 0 {
		v.Add("city", gs.Scenic.City)
		v.Add("keywords", gs.Scenic.ScenicName)
	} else {
		v.Add("keywords", gs.Scenic.Address + gs.Scenic.ScenicName)
	}
	return fmt.Sprint(gs.TargetUrl + v.Encode())

}

func NewSpider() *gspider {
	return new(gspider)
}

func (gs *gspider)GetAgent() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return userAget[r.Intn(len(userAget) - 1)]
}
